package com.s3.demo.service;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.util.IOUtils;
import com.s3.demo.utils.AWSV4Auth;
import com.s3.demo.utils.ResourcesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceException;
import java.io.*;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TreeMap;

/**
 * @author lzz
 * @create 2021-09-06 14:56
 */
@Service
public class AuthServiceImpl implements AuthService {
    private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);
    private static  final String nextLine = "\r\n";
    private static  final String towHyphens = "--";
    private static  final String boudary = "********";


    @Override
    public void doUpload(HttpServletRequest request, String bucketName, String fileNmae) throws NoSuchAlgorithmException {
        String authorization = request.getHeader("authorization");
        if (StringUtils.isEmpty(authorization)) {
            throw new WebServiceException("authorization fail");
        }
        String str = authorization.substring(authorization.indexOf(" "));
        String[] strings = str.split(",");
        String credential = strings[0].substring(strings[0].indexOf("=")+1);
        String[] split = credential.split("/");
        String ak = split[0];
        TreeMap<String, List<String>> params = new TreeMap<String, List<String>>();
        TreeMap<String, String> awsHeaders = new TreeMap<String, String>();
        String signHeaders = strings[1].substring(strings[1].indexOf("=")+1);
        String[] split1 = signHeaders.split(";");
        for (String headerName: split1) {
            awsHeaders.put(headerName, request.getHeader(headerName));
        }
        AWSV4Auth awsv4Auth = new AWSV4Auth.Builder(ak, ResourcesUtil.getResourceStr("test", ak + ".main.sk"))
                .regionName(split[2])
                .serviceName(split[3])
                .httpMethodName("PUT")
                .canonicalURI("/" + bucketName + "/" + fileNmae)
                .queryParametes(params)
                .awsHeaders(awsHeaders)
                .payload(null)
                .xAmzDate(request.getHeader("x-amz-date"))
                .currentDate(split[1])
                .debug()
                .build();
        String signature = awsv4Auth.getSignature();
        String oldSignature = strings[2].substring(strings[2].indexOf("=") + 1);
        if (signature.equals(oldSignature)) {
            try {
                upload(request, ak,fileNmae);
            } catch (IOException e) {
                System.out.println("文件上传失败");
            }
        }
    }

    @Override
    public void preSignDoUpload(HttpServletRequest request, String bucketName, String fileName, MultipartFile file) throws NoSuchAlgorithmException, IOException {
        String credential = request.getParameter("X-Amz-Credential");
        String[] credentialSplit = credential.split("/");
        String oldSignature = request.getParameter("X-Amz-Signature");
        String accessKey = credentialSplit[0];
        TreeMap<String, String> awsHeaders = new TreeMap<String, String>();
        String signHeadersName = request.getParameter("X-Amz-SignedHeaders");
        awsHeaders.put(signHeadersName, request.getHeader(signHeadersName));

        TreeMap<String, List<String>> params = new TreeMap<String, List<String>>();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String name = parameterNames.nextElement();
            if ("X-Amz-Signature".equals(name)) {
                continue;
            }
            String parameter = request.getParameter(name);
            List<String> parameters = new ArrayList<>();
            parameters.add(parameter);
            params.put(name, parameters);
        }

        AWSV4Auth awsv4Auth = new AWSV4Auth.Builder(accessKey, ResourcesUtil.getResourceStr("test", accessKey + ".main.sk"))
                .regionName(credentialSplit[2])
                .serviceName(credentialSplit[3])
                .httpMethodName("GET")
                .canonicalURI("/" + bucketName + "/" + fileName)
                .queryParametes(params)
                .awsHeaders(awsHeaders)
                .payload(null)
                .xAmzDate(request.getParameter("X-Amz-Date"))
                .currentDate(credentialSplit[1])
                .debug()
                .build();
        String signature = awsv4Auth.getPreSignature();
        if (oldSignature.equals(signature)) {
            doPreSignUpload(request,accessKey,file);
        }
    }

    /**
     * 进行预签名上传
     * @param accessKey
     * @param file
     */
    private void doPreSignUpload(HttpServletRequest request,String accessKey, MultipartFile file) throws IOException {

        InputStream inputStream = file.getInputStream();
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        byte[] buffer = new byte[1024];
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\preTest.jpg");
        int len = -1;
        while ((len = dataInputStream.read(buffer)) != -1) {
            fileOutputStream.write(buffer, 0, len);
        }
        fileOutputStream.flush();
        fileOutputStream.close();
    }

    /**
     * 上传文件
     * @param request
     * @param ak
     */
    private void upload(HttpServletRequest request, String ak,String fileName) throws IOException {
        String header = request.getHeader("x-amz-decoded-content-length");

        // 块大小
        int chunkSize = 128 * 1024;
        // 原始流大小
        int decodedLen = Integer.parseInt(header);
        // 块的数量
        int intMaxChunkSize = decodedLen / chunkSize;
        /**
         * awschunkedEncodingInputStream 会在每块的内容头尾加上前面
         * 头的签名长度:
         *Long.toHexString(chunkDataSize).length()
         *                 + CHUNK_SIGNATURE_HEADER.length()
         *                 + SIGNATURE_LENGTH
         *                 + CRLF.length()
         * 尾的签名长度:
         * CRLF.length()+
         *Long.toHexString(chunkDataSize).length()
         *                 + CHUNK_SIGNATURE_HEADER.length()
         *                 + SIGNATURE_LENGTH
         *                 + CRLF.length()
         */
        //块头的签名长度
        int signLen = Long.toHexString(decodedLen).length()
                + ";chunk-signature=".length()
                + 64
                + "\r\n".length();

        ServletInputStream inputStream = request.getInputStream();
        System.out.println("request.getContentLength() = " + request.getContentLength());

        FileOutputStream fileOutputStream = new FileOutputStream("D:\\test1.jpg");
        byte[] media = null;
        int flag = 0;
        int max1024 = decodedLen / 1024;
        int residue = decodedLen % 1024;
        if (residue != 0) {
            max1024 += 1;
        }
        int count1024 = 1;
        //当前处理模式是单块处理
        while (true) {
            //处理头部的签名,不写入流
            if (flag == 0) {
                media = new byte[signLen];
                inputStream.read(media, 0, signLen);
            }else{
                //文件流 分批写入 最后一次写入的时候要判断是否满1024字节
                if (count1024 == max1024&&residue != 0) {
                    media = new byte[residue];
                    inputStream.read(media, 0, residue);
                    fileOutputStream.write(media);
                    break;
                }else{
                    media = new byte[1024];
                    inputStream.read(media, 0, 1024);
                    fileOutputStream.write(media);
                    if (count1024 == max1024) {
                        break;
                    }
                }
                count1024++;
            }
            flag++;
        }
        fileOutputStream.close();
        inputStream.close();

        upload(ak, fileName);

    }

    /**
     * 上传
     * @param ak 主的accessKey
     * @param fileName 上传至备机的keyName
     */
    public static void upload(String ak,String fileName) {
        AWSCredentials credentials = new BasicAWSCredentials(ResourcesUtil.getResourceStr("test",ak+".slave.ak"), ResourcesUtil.getResourceStr("test",ak+".slave.sk"));
        ClientConfiguration clientConfiguration = new ClientConfiguration();
        clientConfiguration.setSignerOverride("AWSS3V4SignerType");

        AmazonS3 s3Client = AmazonS3ClientBuilder
                .standard()
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://localhost:9090", Regions.US_EAST_1.name()))
                .withPathStyleAccessEnabled(true)
                .withClientConfiguration(clientConfiguration)
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();
        System.out.println("Uploading a new object to S3 from a file\n");
        File file = new File("D:\\test1.jpg");
        // 上传文件
        s3Client.putObject(new PutObjectRequest(ResourcesUtil.getResourceStr("test",ak+".slave.bucket"), fileName, file));
    }


    @Override
    public void doDownload(String bucketName, String fileName, HttpServletRequest request, HttpServletResponse response) throws NoSuchAlgorithmException {
        String authorization = request.getHeader("authorization");
        if (StringUtils.isEmpty(authorization)) {
            throw new WebServiceException("authorization fail");
        }
        String str = authorization.substring(authorization.indexOf(" "));
        String[] strings = str.split(",");
        String credential = strings[0].substring(strings[0].indexOf("=")+1);
        String[] split = credential.split("/");
        String ak = split[0];
        TreeMap<String, List<String>> params = new TreeMap<String, List<String>>();
        TreeMap<String, String> awsHeaders = new TreeMap<String, String>();
        String signHeaders = strings[1].substring(strings[1].indexOf("=")+1);
        String[] split1 = signHeaders.split(";");
        for (String headerName: split1) {
            awsHeaders.put(headerName, request.getHeader(headerName));
        }
        AWSV4Auth awsv4Auth = new AWSV4Auth.Builder(ak, ResourcesUtil.getResourceStr("test", ak + ".main.sk"))
                .regionName(split[2])
                .serviceName(split[3])
                .httpMethodName("GET")
                .canonicalURI("/" + bucketName + "/" + fileName)
                .queryParametes(params)
                .awsHeaders(awsHeaders)
                .payload(null)
                .xAmzDate(request.getHeader("x-amz-date"))
                .currentDate(split[1])
                .debug()
                .build();
        String signature = awsv4Auth.getSignature();
        String oldSignature = strings[2].substring(strings[2].indexOf("=") + 1);
        if (signature.equals(oldSignature)) {
            System.out.println("签名比对成功~~~~~");
        }
        //将文件下载到本地
        saveToLocalByS3(ak,bucketName,fileName);
        try {
            //下载获取文件流
            response.reset();
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            OutputStream outputStream = response.getOutputStream();
            //流写入
            InputStream inputStream=new FileInputStream("D:\\"+fileName);
            response.setContentLength(inputStream.available());
            IOUtils.copy(inputStream,outputStream);
            response.flushBuffer();
            inputStream.close();
            outputStream.close();
        } catch (Exception ex) {
            logger.info("下载失败：", ex.getMessage());
        }

    }
    /**
     * 通过s3 将文件下载到本地
     * @param bucketName
     * @param fileName
     */
    private void saveToLocalByS3(String ak,String bucketName, String fileName) {
        AWSCredentials credentials = new BasicAWSCredentials(ResourcesUtil.getResourceStr("test",ak+".slave.ak"), ResourcesUtil.getResourceStr("test",ak+".slave.sk"));
        ClientConfiguration clientConfiguration = new ClientConfiguration();
        clientConfiguration.setSignerOverride("AWSS3V4SignerType");

        AmazonS3 s3Client = AmazonS3ClientBuilder
                .standard()
                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://192.168.200.170:9000", Regions.US_EAST_1.name()))
                .withPathStyleAccessEnabled(true)
                .withClientConfiguration(clientConfiguration)
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();
        System.out.println("Uploading a new object to S3 from a file\n");
        // 下载文件
        GetObjectRequest rangeObjectRequest = new GetObjectRequest(ResourcesUtil.getResourceStr("test",ak+".slave.bucket"), fileName);
        S3Object object = s3Client.getObject(rangeObjectRequest);

        File file=new File("D:\\"+fileName);
        s3Client.getObject(rangeObjectRequest,file);
    }
}
